<?php
namespace library;

use Monolog\Logger;
use Noodlehaus\Exception;

class DaemonCore
{
    static $_processId;
    static $_processIsChild;
    static $_logger;
    static $_isDying;
    static $_appName;
    static $_pcount = 0;

    static public function start( ){

        if ((php_sapi_name() != 'cli')) {
            self::$_logger->error("只能使用php-cli模式进行启动");
            throw new Exception("只能使用php-cli模式进行启动");
        }

        if (!function_exists('posix_getpid')) {
            self::$_logger->error("php未安装posix扩展,请编译安装");
            throw new Exception("php未安装posix扩展,请编译安装");
        }

        if (function_exists('gc_enable')) {
            self::$_logger->info("启动gc.....",['success']);
            gc_enable();
        }

        self::$_processId      = 0;
        self::$_processIsChild = false;
        self::$_isDying        = false;
        self::$_logger->info("启动fork.....",['success']);

        $forkRes = self::fork();
        if($forkRes == false){
            self::$_logger->error("启动fork失败",['fail']);
            return false;
        }

        if (!is_numeric(self::$_processId) || self::$_processId < 1) {
            self::$_logger->error("pid异常,运行失败",[self::$_processId]);
            return false;
        }

        //赋予最大的权限
        self::$_logger->info("umask变更.....",['success']);
        @umask(0);

        //保存pid文件
        self::$_logger->info("保存pid文件.....",['success']);
        self::savePidFile(self::$_processId);

        //变更工作目录
        self::$_logger->info("变更工作目录.....",['success']);
        @chdir(ROOT_PATH);

        //终止信号 9
        self::$_logger->info("注册终止信号.....",['success']);
        pcntl_signal(SIGTERM, function (){
            self::$_isDying = true;
            self::$_logger->info("信号处理：进程被终止");
            die();
        });

        self::$_logger->info("注册重启信号.....",['success']);
        pcntl_signal(SIGHUP, function (){
            self::$_logger->info("信号处理：进程重启");

        });

        self::$_logger->info("注册回收工作进程.....",['success']);
        pcntl_signal(SIGCHLD, function (){
            self::$_logger->info("信号处理：回收工作进程");
            while (($pid=pcntl_waitpid(-1, $status, WNOHANG))> 0) {

                DaemonCore::subSonPthread($pid);
                usleep(1000);
            }
        });

        self::$_logger->info("完成初始化.....",['success']);
        return true;
    }

    static public function iterate($sleepSeconds = 1)
    {
        //1s 休眠1秒
        sleep($sleepSeconds);

        //文件cache写回
        clearstatcache();

        // Garbage Collection (PHP >= 5.3)
        if (function_exists('gc_collect_cycles')) {
            //启动垃圾回收
            gc_collect_cycles();
        }

        //注册信号处理
        pcntl_signal_dispatch();

        self::$_logger->debug("休眠1秒.....",['success']);
        return true;
    }

    static public function isRuning(){
        //检查pid文件是否存在
        self::$_logger->debug("检查pid文件是否存在.....");
        if (!file_exists(PID_FILE_PATH)) {
            self::$_logger->debug("检查进程是否运行：未运行",["pid文件不存在"]);
            return false;
        }
        self::$_logger->debug("检查pid文件是否存在.....",["success"]);

        //读取pid的值
        self::$_logger->debug("读取pid的值.....");
        $pid = self::fileread(PID_FILE_PATH);
        if (!$pid) {
            self::$_logger->debug("检查进程是否运行：未运行",["读取不到pid的值"]);
            return false;
        }
        self::$_logger->debug("检查pid文件是否存在.....",[$pid]);

        // 检查进程是否存在，存在返回true, 反之返回false
        self::$_logger->debug("检查进程是否存在.....");
        if (!posix_kill(intval($pid), 0)) {
            @unlink(PID_FILE_PATH);
            self::$_logger->debug("检查进程是否运行：未运行",["进程未运行"]);
            return  false;
        }

        self::$_logger->debug("检查进程是否存在.....",["存在"]);
        return true;
    }

    static public function fileread ($path) {
        $file = fopen($path, 'r');
        if (!$file) return false;
        $val = fread($file, filesize($path));
        fclose($file);
        return $val;
    }

    static public function stop( )
    {
        if(self::isRuning() == false){
            self::$_logger->info("进程已关闭");
            die("进程已关闭");
        }
        //读取pid的值
        $pid = self::fileread(PID_FILE_PATH);
        if (!$pid) {
            self::$_logger->info("进程已关闭");
            die("进程已关闭");
        }

        // 进程存在，存在返回true, 反之返回false
        if (!posix_kill(intval($pid), 0)) {
            @unlink(PID_FILE_PATH);
            self::$_logger->info("进程已关闭");
            die("进程已关闭");
        }

        passthru('kill -9 ' . $pid);

        die("进程已关闭");
    }


    static protected function savePidFile($pid = null)
    {
        if (empty($pid)) {
            self::$_logger->error("保持pid文件失败,pid为空");
            return false;
        }

        if (!file_put_contents(PID_FILE_PATH, $pid)) {
            self::$_logger->error('无法写入pid文件: %s', [PID_FILE_PATH]);
            return false;
        }

        if (!chmod(PID_FILE_PATH, 0755)) {
            self::$_logger->error('无法更改pid目录权限: %s', [PID_FILE_PATH]);;
            return false;
        }

        return true;
    }



    static protected function fork()
    {
        $pid = pcntl_fork();
        if ($pid === -1) {
            // Error
            self::$_logger->error("启动fork.....失败",['fail']);

            return false;
        } else if ($pid) {

            self::$_logger->info("终端进程退出",['success']);
            exit();
        } else {
            // Child
            self::$_processIsChild = true;
            self::$_isDying        = false;
            self::$_processId      = posix_getpid();
            self::$_logger->info("Daemon主进程启动完成",['进程id'=>self::$_processId]);
        }

        return true;
    }


    static public function getSonPthreadCount(){
        self::$_logger->debug("统计当前工作进程数量",[self::$_pcount]);
        return self::$_pcount;
    }

    static public function subSonPthread($pid){
        self::$_logger->debug("结束工作进程",[$pid]);
        self::$_pcount ++;
    }

    static public function addSonPthread($pid){
        self::$_logger->debug("新增工作进程",[$pid]);
        self::$_pcount --;
    }


}